/*
 * Copyright (c) 2010-2013 Ruben Chakhmakhchyan. All rights reserved.
 * 
 * This file contains Original Code and/or Modifications of Original Code
 * as defined in and that are subject to the Eclipse Public License
 * Version 1.0 (the 'License'). You may not use this file except in
 * compliance with the License. Please obtain a copy of the License at
 * http://opensource.org/licenses/EPL-1.0 and read it before using this file.
 */

#include "scripting.h"
#include "plugin_impl.h"
#include "strings.h"
#include <Shobjidl.h>
#include <Shellapi.h>
#include "scripting_utils.h"


#define NS( n ) ( name == npid.##n )

#define NS1( n ) { npid.##n, n },



ScriptablePluginObject::ScriptablePluginObject( NPP npp )
    : NPObjectBase( npp ),
    m_npoOnScanComplete( NULL ),
    m_npoOnImageAcquired( NULL ),
#ifndef _ADDON
    m_npoOnUploadComplete( NULL ),
    m_npoOnUploadFailed( NULL ),
#endif
    m_npoOnSelectedImageChanged( NULL ),
    m_npoOnImageCountChanged( NULL ),
    m_npoOnError( NULL ),
    m_npoOnDeviceEvent( NULL ),
    m_npoOnSaveComplete( NULL ),
    m_npoOnZoomChange( NULL ),
    m_npoOnToolStateChange( NULL ),
    m_npoDeviceIndexer( NULL ),
    m_npoImageIndexer( NULL )
{

}

ScriptablePluginObject::~ScriptablePluginObject()
{
    ::NPN_ReleaseObject( m_npoOnScanComplete );
    ::NPN_ReleaseObject( m_npoOnImageAcquired );
#ifndef _ADDON
    ::NPN_ReleaseObject( m_npoOnUploadComplete );
    ::NPN_ReleaseObject( m_npoOnUploadFailed );
#endif
    ::NPN_ReleaseObject( m_npoOnSelectedImageChanged );
    ::NPN_ReleaseObject( m_npoOnImageCountChanged );
    ::NPN_ReleaseObject( m_npoOnError );
    ::NPN_ReleaseObject( m_npoOnDeviceEvent );
    ::NPN_ReleaseObject( m_npoOnZoomChange );
    ::NPN_ReleaseObject( m_npoOnToolStateChange );

    ::NPN_ReleaseObject( m_npoDeviceIndexer );
}

bool ScriptablePluginObject::hasMethod( NPIdentifier name )
{
    return NS( testMethod )
        || NS( scan ) 
#ifndef _ADDON
        || NS( upload )
#endif
        || NS( getDevices ) || NS( saveToFile ) || NS( saveToArray )
        || NS( pan ) || NS( panX ) || NS( panY ) || NS( rotate ) || NS( crop )
        || NS( selectImage ) || NS( selectNextImage ) || NS( selectPreviousImage )
        || NS( getCap )
        || NS( connect ) || NS( disconnect )
        || NS( clear )
        || NS( getFilter ) || NS( setFilter )
        || NS( resume ) || NS( stop ) || NS( pause )
        || NS( initializeDevice )
        || NS( getFolder ) || NS( openFolder )
        || NS( focus )
        || NS( deleteImage ) || NS( shiftImage ) || NS( moveImage );
}

bool ScriptablePluginObject::hasProperty( NPIdentifier name )
{
    return NS( testProperty )
        || NS( onScanComplete ) || NS( onImageAcquired ) || NS( onSelectedImageChanged ) || NS( onImageCountChanged ) || NS( onZoomChanged ) || NS( onToolStateChanged )
#ifndef _ADDON
        || NS( onUploadComplete ) || NS( onUploadFailed ) 
#endif
        || NS( onError ) || NS( onDeviceEvent ) || NS( onSaveComplete )
        || NS( pixelType ) || NS( resolution ) || NS( contrast ) || NS( orientation )
        || NS( devices ) || NS( deviceIndex ) || NS( deviceType ) || NS( showDeviceUI )
        || NS( zoomTool ) || NS( zoom ) || NS( fitZoom ) || NS( panTool ) || NS( cropTool )
        || NS( driverModel )
        || NS( imageQuality ) || NS( autoPosition )
        || NS( paperSource )
        || NS( images )
        || NS( version )
        || NS( autoCompress ) || NS( autoCompressionLevel ) || NS( autoCompressInBackground )
        || NS( autoPause )
        || NS( selectedIndex )
        || NS( thumbnailSize )
        || NS( isFlatbedSupported ) || NS( isFeederSupported ) || NS( isDuplexSupported )
        || NS( pageWidth ) || NS( pageHeight )
        || NS( isConnected )
        || NS( isPlatformSupported );
}

bool ScriptablePluginObject::getProperty( NPIdentifier name, NPVariant *result )
{
    NPVariant& resultValue = *result;
    VOID_TO_NPVARIANT( resultValue );

    PluginModel* pModel = m_pPlugin->m_model;
    ImageAcquisitionModel* iaModel = pModel->getIaModel();
    bool iaModelActive = iaModel != NULL;

    if NS( onScanComplete )
    {
        OBJECT_TO_NPVARIANT( m_npoOnScanComplete, resultValue );
    }
    else if NS( onImageAcquired )
    {
        OBJECT_TO_NPVARIANT( m_npoOnImageAcquired, resultValue );
    }
    else if NS( onSelectedImageChanged )
    {
        OBJECT_TO_NPVARIANT( m_npoOnSelectedImageChanged, resultValue );
    }
#ifndef _ADDON
    else if NS( onUploadComplete )
    {
        OBJECT_TO_NPVARIANT( m_npoOnUploadComplete, resultValue );
    }
    else if NS( onUploadFailed )
    {
        OBJECT_TO_NPVARIANT( m_npoOnUploadFailed, resultValue );
    }
#endif
    else if NS( onImageCountChanged )
    {
        OBJECT_TO_NPVARIANT( m_npoOnImageCountChanged, resultValue );
    }
    else if NS( onDeviceEvent )
    {
        OBJECT_TO_NPVARIANT( m_npoOnDeviceEvent, resultValue );
    }
    else if NS( onZoomChanged )
    {
        OBJECT_TO_NPVARIANT( m_npoOnZoomChange, resultValue );
    }
    else if NS( onSaveComplete )
    {
        OBJECT_TO_NPVARIANT( m_npoOnSaveComplete, resultValue );
    }
    else if NS( onToolStateChanged )
    {
        OBJECT_TO_NPVARIANT( m_npoOnToolStateChange, resultValue );
    }
    else if NS( onError )
    {
        OBJECT_TO_NPVARIANT( m_npoOnError, resultValue );
    }
    else if NS( pixelType )
    {
        if ( iaModelActive )
        {
            PixelType pixelType = pModel->getPixelType();
            INT32_TO_NPVARIANT( ( int ) pixelType, resultValue );
        }
    }
    else if NS( resolution )
    {
        if ( iaModelActive )
        {
            int value = pModel->getResolution();
            INT32_TO_NPVARIANT( ( int ) value, resultValue );
        }
    }
    else if NS( transferMode )
    {
        if ( iaModelActive )
        {
            TransferMode value = pModel->getTransferMode();
            INT32_TO_NPVARIANT( ( int ) value, resultValue );
        }
    }
    else if NS( devices )
    {
        if ( m_npoDeviceIndexer == NULL )
        {
            NPObject *npoDeviceIndexer = ::NPN_CreateObject( m_npp, GET_NPOBJECT_CLASS( ScriptableDeviceIndexer ) );
            ::NPN_RetainObject( npoDeviceIndexer );
            m_npoDeviceIndexer = ( ScriptableDeviceIndexer* ) npoDeviceIndexer;
            m_npoDeviceIndexer->setModel( pModel );
        }
        ::NPN_RetainObject( m_npoDeviceIndexer );

        OBJECT_TO_NPVARIANT( m_npoDeviceIndexer, resultValue );
    }
    else if NS( images )
    {
        if ( m_npoImageIndexer == NULL )
        {
            NPObject *npoImageIndexer = ::NPN_CreateObject( m_npp, GET_NPOBJECT_CLASS( ScriptableImageIndexer ) );
            ::NPN_RetainObject( npoImageIndexer );
            m_npoImageIndexer = ( ScriptableImageIndexer* ) npoImageIndexer;
            m_npoImageIndexer->setModel( pModel );
        }
        ::NPN_RetainObject( m_npoImageIndexer );

        OBJECT_TO_NPVARIANT( m_npoImageIndexer, resultValue );
    }
    else if NS( zoomTool )
    {
        BOOLEAN_TO_NPVARIANT( pModel->getZoomState(), resultValue );
    }
    else if NS( zoom )
    {
        DOUBLE_TO_NPVARIANT( ( double ) pModel->getZoom(), resultValue );
    }
    else if NS( fitZoom )
    {
        DOUBLE_TO_NPVARIANT( ( double ) pModel->getZoomFit(), resultValue );
    }
    else if NS( panTool )
    {
        BOOLEAN_TO_NPVARIANT( pModel->getPanState(), resultValue );
    }
    else if NS( cropTool )
    {
        BOOLEAN_TO_NPVARIANT( pModel->getCropState(), resultValue );
    }
    else if NS( driverModel )
    {
        INT32_TO_NPVARIANT( pModel->getIaModel()->getModelTypeId(), resultValue );
    }
    else if NS( showDeviceUI )
    {
        BOOLEAN_TO_NPVARIANT( pModel->getShowDeviceUI(), resultValue );
    }
    else if NS( deviceIndex )
    {
        if ( iaModelActive )
        {
            INT32_TO_NPVARIANT( pModel->getDeviceIndex(), resultValue );
        }
    }
    else if NS( paperSource )
    {
        if ( iaModelActive )
        {
            PaperSource paperSource = pModel->getPaperSource();
            int result = 0;
            if ( paperSource == PaperSource::Flatbed )
            {
                result = 1;
            }
            else if ( paperSource == PaperSource::Feeder )
            {
                result = 2;
            }
            INT32_TO_NPVARIANT( result, resultValue );
        }
    }
    else if NS( imageQuality )
    {
        if ( iaModelActive )
        {
            int value = ( int ) pModel->getImageQuality();
            INT32_TO_NPVARIANT( value, resultValue );
        }
    }
    else if NS( contrast )
    {
        if ( iaModelActive )
        {
            int value = ( int ) pModel->getContrast();
            INT32_TO_NPVARIANT( value, resultValue );
        }
    }
    else if NS( version )
    {
        char* npVersion = ScriptingUtils::npstring( pModel->getVersion() );
        STRINGZ_TO_NPVARIANT( npVersion, resultValue );
    }
    else if NS( autoCompress )
    {
        BOOLEAN_TO_NPVARIANT( pModel->getAutoCompress(), resultValue );
    }
    else if NS( autoPause )
    {
        BOOLEAN_TO_NPVARIANT( pModel->getAutoPause(), resultValue );
    }
    else if NS( autoCompressionLevel )
    {
        BOOLEAN_TO_NPVARIANT( pModel->getAutoCompressionLevel(), resultValue );
    }
    else if NS( autoCompressInBackground )
    {
        BOOLEAN_TO_NPVARIANT( pModel->getAutoCompressInBackground(), resultValue );
    }
    else if NS( selectedIndex )
    {
        INT32_TO_NPVARIANT( pModel->getSelectedImageIndex(), resultValue );
    }
    else if NS( thumbnailSize )
    {
        INT32_TO_NPVARIANT( pModel->getImageThumbnailSize(), resultValue );
    }
    else if NS( isFlatbedSupported )
    {
        if ( pModel->getIaModel() && pModel->getDeviceIndex() > 0 )
        {
            BOOLEAN_TO_NPVARIANT( pModel->getIaModel()->getIsDeviceFeatureSupported( DeviceFeature::Flatbed ), resultValue );
        }
    }
    else if NS( isFeederSupported )
    {
        if ( pModel->getIaModel() && pModel->getDeviceIndex() > 0 )
        {
            BOOLEAN_TO_NPVARIANT( pModel->getIaModel()->getIsDeviceFeatureSupported( DeviceFeature::Feeder ), resultValue );
        }
    }
    else if NS( isDuplexSupported )
    {
        if ( pModel->getIaModel() && pModel->getDeviceIndex() > 0 )
        {
            BOOLEAN_TO_NPVARIANT( pModel->getIaModel()->getIsDeviceFeatureSupported( DeviceFeature::Duplex ), resultValue );
        }
    }
    else if NS( pageWidth )
    {
        DOUBLE_TO_NPVARIANT( pModel->getPageWidth(), resultValue );
    }
    else if NS( pageHeight )
    {
        DOUBLE_TO_NPVARIANT( pModel->getPageHeight(), resultValue );
    }
    else if NS( isConnected )
    {
        if ( iaModel )
        {
            BOOLEAN_TO_NPVARIANT( iaModel->getIsConnected(), resultValue );
        }
    }
    else if NS( orientation )
    {
        if ( iaModel )
        {
            INT32_TO_NPVARIANT( ( int ) iaModel->getOrientation(), resultValue );
        }
    }
    else if NS( isPlatformSupported )
    {
        BOOLEAN_TO_NPVARIANT( pModel->getPlatformSupported(), resultValue );
    }
    else
    {
        return false;
    }

    return true;
}

bool ScriptablePluginObject::setProperty( NPIdentifier name, const NPVariant *pVariant )
{
    NPVariant& v = * ( ( NPVariant* ) pVariant );
    PluginModel* pModel = m_pPlugin->m_model;
    ImageAcquisitionModel* iaModel = pModel->getIaModel();
    bool iaModelActive = iaModel != NULL;

#define SET_EVENT( eventVar ) \
    if ( NPVARIANT_IS_OBJECT( v ) )\
    {\
    NPObject* npo = NPVARIANT_TO_OBJECT( v );\
    eventVar = npo;\
    ::NPN_RetainObject( npo );\
    }\
    return true;\


    if NS( onScanComplete )
    {
        SET_EVENT( m_npoOnScanComplete )
    }
    else if NS( onImageAcquired )
    {
        SET_EVENT( m_npoOnImageAcquired )
    }
    else if NS( onSelectedImageChanged )
    {
        SET_EVENT( m_npoOnSelectedImageChanged )
    }
    else if NS( onImageCountChanged )
    {
        SET_EVENT( m_npoOnImageCountChanged )
    }
#ifndef _ADDON
    else if NS( onUploadComplete )
    {
        SET_EVENT( m_npoOnUploadComplete )
    }
    else if NS( onUploadFailed )
    {
        SET_EVENT( m_npoOnUploadFailed )
    }
#endif
    else if NS( onDeviceEvent )
    {
        SET_EVENT( m_npoOnDeviceEvent )
    }
    else if NS( onZoomChanged )
    {
        SET_EVENT( m_npoOnZoomChange )
    }
    else if NS( onToolStateChanged )
    {
        SET_EVENT( m_npoOnToolStateChange )
    }
    else if NS( onSaveComplete )
    {
        SET_EVENT( m_npoOnSaveComplete )
    }
    else if NS( onError )
    {
        SET_EVENT( m_npoOnError )
    }
    else if NS( pixelType )
    {
        if ( IS_NUMERIC( v ) )
        {
            int intValue = AS_INT( v );

            pModel->setPixelType( ( PixelType ) intValue );

            return true;
        }
    }
    else if NS( resolution )
    {
        if ( IS_NUMERIC( v ) )
        {
            int intValue = AS_INT( v );

            pModel->setResolution( intValue );

            return true;
        }
    }
    else if NS( transferMode )
    {
        if ( NPVARIANT_IS_INT32( v ) )
        {
            int intValue = NPVARIANT_TO_INT32( v );

            pModel->setTransferMode( ( TransferMode ) intValue );

            return true;
        }
    }
    else if NS( zoomTool )
    {
        if ( NPVARIANT_IS_BOOLEAN( v ) )
        {
            bool value = NPVARIANT_TO_BOOLEAN( v );

            pModel->setZoomState( value, false );

            return true;
        }
    }
    else if NS( zoom )
    {
        double zoomValue;
        if ( NPVARIANT_IS_INT32( v ) || NPVARIANT_IS_DOUBLE( v ) )
        {
            zoomValue = NPVARIANT_IS_INT32( v ) ? v.value.intValue : v.value.doubleValue;

            pModel->setZoom( zoomValue / 100.0F );

            return true;
        }
    }
    else if NS( panTool )
    {
        if ( NPVARIANT_IS_BOOLEAN( v ) )
        {
            bool value = NPVARIANT_TO_BOOLEAN( v );

            pModel->setPanState( value, false );

            return true;
        }
    }
    else if NS( cropTool )
    {
        if ( NPVARIANT_IS_BOOLEAN( v ) )
        {
            bool value = NPVARIANT_TO_BOOLEAN( v );

            pModel->setCropState( value, false );

            return true;
        }
    }
    else if NS( driverModel )
    {
        if ( IS_NUMERIC( v ) )
        {
            pModel->setDriverModel( AS_INT( v ) );

            return true;
        }
    }
    else if NS( showDeviceUI )
    {
        if ( NPVARIANT_IS_BOOLEAN( v ) )
        {
            bool value = NPVARIANT_TO_BOOLEAN( v );
            pModel->setShowDeviceUI( value );

            return true;
        }
    }
    else if ( NS( deviceIndex ) )
    {
        if ( IS_NUMERIC( v ) )
        {
            pModel->setDeviceIndex( AS_INT( v ) );

            return true;
        }
    }
    else if NS( paperSource )
    {
        if ( IS_NUMERIC( v ) )
        {
            if ( iaModelActive )
            {
                int value = AS_INT( v );

                if ( value >= 0 && value <= 2 )
                {
                    pModel->setPaperSource( ( PaperSource ) value );
                }
            }
            return true;
        }
    }
    else if NS( imageQuality )
    {
        if ( IS_NUMERIC( v ) )
        {
            if ( iaModelActive )
            {
                pModel->setImageQuality( ( uint_t ) AS_INT( v ) );
            }
            return true;
        }
    }
    else if NS( contrast )
    {
        if ( IS_NUMERIC( v ) )
        {
            if ( iaModelActive )
            {
                pModel->setContrast( AS_INT( v ) );
            }
            return true;
        }
    }
    else if NS( autoCompress )
    {
        if ( NPVARIANT_IS_BOOLEAN( v ) )
        {
            bool value = NPVARIANT_TO_BOOLEAN( v );
            pModel->setAutoCompress( value );

            return true;
        }
    }
    else if NS( autoPause )
    {
        if ( NPVARIANT_IS_BOOLEAN( v ) )
        {
            bool value = NPVARIANT_TO_BOOLEAN( v );
            pModel->setAutoPause( value );

            return true;
        }
    }
    else if NS( autoCompressionLevel )
    {
        if ( NPVARIANT_IS_BOOLEAN( v ) )
        {
            bool value = NPVARIANT_TO_BOOLEAN( v );
            pModel->setAutoCompressionLevel( value );

            return true;
        }
    }
    else if NS( autoCompressInBackground )
    {
        if ( NPVARIANT_IS_BOOLEAN( v ) )
        {
            bool value = NPVARIANT_TO_BOOLEAN( v );
            pModel->setAutoCompressInBackground( value );

            return true;
        }
    }
    else if NS( thumbnailSize )
    {
        if ( IS_NUMERIC( v ) )
        {
            int value = AS_INT( v );
            pModel->setImageThumbnailSize( ( uint_t ) value );

            return true;
        }
    }
    else if NS( pageWidth )
    {
        double value = 0;
        if ( NPVARIANT_IS_DOUBLE( v ) )
        {
            value = NPVARIANT_TO_DOUBLE( v );
        }
        else if ( NPVARIANT_IS_INT32( v ) )
        {
            value = NPVARIANT_TO_INT32( v );
        }

        if ( value > 0 )
        {
            pModel->setPageWidth( value );
            return true;
        }
    }
    else if NS( pageHeight )
    {
        double value = 0;
        if ( NPVARIANT_IS_DOUBLE( v ) )
        {
            value = NPVARIANT_TO_DOUBLE( v );
        }
        else if ( NPVARIANT_IS_INT32( v ) )
        {
            value = NPVARIANT_TO_INT32( v );
        }

        if ( value > 0 )
        {
            pModel->setPageHeight( value );
            return true;
        }
    }
    else if NS( orientation )
    {
        if ( IS_NUMERIC( v ) )
        {
            if ( iaModel )
            {
                int value = AS_INT( v );
                iaModel->setOrientation( value );
            }
            return true;
        }
    }

    return false;
}

bool ScriptablePluginObject::invoke( NPIdentifier name, const NPVariant *args, uint32_t argCount, NPVariant *result )
{
    _Plugin* pPlugin = ( _Plugin* ) m_npp->pdata;
    PluginModel* pModel = pPlugin->getModel();

    NPVariant& resultValue = *result;

    if NS( scan )
    {
        //pPlugin->m_model->scan( true );

        // async
        m_pPlugin->m_model->beginScan( true, ::scanCallback );

        return true;
    }
#ifndef _ADDON
    else if NS( upload )
    {
        if ( argCount == 5 )
        {
            NPVariant npvPages = *args, npvFilename = args[ 1 ], npvFormat = args[ 2 ], npvUrl = args[ 3 ], npvId = args[ 4 ];

            bool urlIsNull = NPVARIANT_IS_NULL( npvUrl );
            bool urlIsEmpty = NPVARIANT_IS_STRING( npvUrl ) && npvPages.value.stringValue.UTF8Length == 0;
            bool filenameIsNull = NPVARIANT_IS_NULL( npvFilename );
            bool filenameIsString = NPVARIANT_IS_STRING( npvFilename );
            bool filenameIsObject = NPVARIANT_IS_OBJECT( npvFilename );
            bool scopeIsNull = NPVARIANT_IS_NULL( npvPages );
            bool scopeIsInt = NPVARIANT_IS_INT32( npvPages );
            bool scopeIsString = NPVARIANT_IS_STRING( npvPages );
            bool idIsNull = NPVARIANT_IS_NULL( npvId );
            bool idIsInt = NPVARIANT_IS_INT32( npvId );

            const char* urlString = ( const char* ) npvUrl.value.stringValue.UTF8Characters;
            const char* filenameString = ( const char* ) npvFilename.value.stringValue.UTF8Characters;
            NPObject* filenameObject = npvFilename.value.objectValue;
            const char* formatString = ( const char* ) npvFormat.value.stringValue.UTF8Characters;
            const char* pagesString = ( const char* ) npvPages.value.stringValue.UTF8Characters;
            int uploadId = npvId.value.intValue;

            if ( ( urlIsNull || ( NPVARIANT_IS_STRING( npvUrl ) && String::IsUtf8Ascii( urlString ) ) )
                && ( filenameIsNull || filenameIsObject || ( filenameIsString && String::IsUtf8Ascii( filenameString ) ) )
                && ( NPVARIANT_IS_STRING( npvFormat ) && String::IsUtf8Ascii( formatString ) )
                && ( scopeIsNull || scopeIsInt || ( NPVARIANT_IS_STRING( npvPages ) && String::IsUtf8Ascii( pagesString ) ) ) 
                && ( idIsNull || idIsInt ) )
            {
                char* url = NULL;
                if ( !urlIsNull && !urlIsEmpty )
                {
                    String::Copy( &url, urlString );
                }

                DocumentFormat format = DocumentFormat::Default;
                int* indices = NULL;
                int indexCount = 0;
                bool pagesValid = true;

                if ( scopeIsString )
                {
                    pagesValid = pModel->parseIndices( pagesString, indices, indexCount );
                }
                else if ( scopeIsInt )
                {
                    if ( pagesValid = NPVARIANT_TO_INT32( npvPages ) > 0 )
                    {
                        indices = new int;
                        indices[ 0 ] = NPVARIANT_TO_INT32( npvPages );
                        indexCount = 1;
                    }
                }

                // to array of string
                List<char*>* filenameArray = NULL;
                int filenameCount = 0;

                if ( !filenameIsNull && !filenameIsString && ScriptingUtils::npoToArray( m_npp, filenameObject, ArrayType::StringUtf8, ( void** ) &filenameArray ) && ( filenameCount == indexCount ) )
                {
                    // filename array validated
                    filenameCount = filenameArray->count();
                }
                else
                {
                    filenameArray = new List<char*>( 1 );
                    ( *filenameArray )[ 0 ] = NULL;
                    if ( filenameIsString )
                    {
                        char* filenameStringCopy = NULL;
                        String::Copy( &filenameStringCopy, filenameString );
                        ( *filenameArray )[ 0 ] = filenameStringCopy;
                        filenameCount = 1;
                    }
                }

                if ( pModel->stringToDocumentFormat( formatString, format ) && pagesValid )
                {
                    bool uploadStarted = pPlugin->m_model->beginUpload( format, url, filenameArray, indices, indexCount, uploadCallback, uploadId );

                    BOOLEAN_TO_NPVARIANT( uploadStarted, *result );
                }

                return true;

                //pPlugin->upload();
            }
            ::NPN_ReleaseVariantValue( &npvFilename );
            ::NPN_ReleaseVariantValue( &npvFormat );
            ::NPN_ReleaseVariantValue( &npvUrl );
        }
    }
#endif
    else if NS( getDevices )
    {
        if ( !pPlugin->m_model->getDevices() )
            return false;
        return true;
    }
    else if NS( saveToFile ) // "1-7:o,9,10,16-89:e,9,10"  // <range> = N { '-' N { ':' { 'odd' | 'even' } }; <format> = 'all' | ( <range> { ',' <range> } }
    {
        bool started = false;
        if( argCount == 5 )
        {
            NPVariant npvPages = *args, npvFilename = args[ 1 ], npvFormat = args[ 2 ], npvMultipage = args[ 3 ], npvPath = args[ 4 ];

            bool pagesIsInt = IS_NUMERIC( npvPages );
            int pagesInt = AS_INT( npvPages );
            bool pagesIsString = NPVARIANT_IS_STRING( npvPages );
            char* pagesString = NULL;
            if ( pagesIsString )
            {
                pagesString = new char[ npvPages.value.stringValue.UTF8Length + 1 ];
                ::memset( pagesString, 0, npvPages.value.stringValue.UTF8Length + 1 );
                ::memcpy( pagesString, npvPages.value.stringValue.UTF8Characters, npvPages.value.stringValue.UTF8Length );
            }
            bool formatIsString = NPVARIANT_IS_STRING( npvFormat );
            char* formatString = NULL;
            if ( formatIsString )
            {
                formatString = new char[ npvFormat.value.stringValue.UTF8Length + 1 ];
                ::memset( formatString, 0, npvFormat.value.stringValue.UTF8Length + 1 );
                ::memcpy( formatString, npvFormat.value.stringValue.UTF8Characters, npvFormat.value.stringValue.UTF8Length );
            }
            bool filenameIsNull = NPVARIANT_IS_NULL( npvFilename );
            bool filenameIsString = NPVARIANT_IS_STRING( npvFilename );
            bool filenameIsObject = NPVARIANT_IS_OBJECT( npvFilename );
            bool multipageIsBool = NPVARIANT_IS_BOOLEAN( npvMultipage );
            bool multipageIsNull = NPVARIANT_IS_NULL( npvMultipage );
            bool multipageBool = NPVARIANT_TO_BOOLEAN( npvMultipage );

            const char* filenameString = ( const char* ) npvFilename.value.stringValue.UTF8Characters;
            NPObject* filenameObject = npvFilename.value.objectValue;

            if ( ( pagesInt || ( NPVARIANT_IS_STRING( npvPages ) && String::IsUtf8Ascii( pagesString ) ) )
                && ( filenameIsNull || filenameIsObject || ( filenameIsString && String::IsUtf8Ascii( filenameString ) ) )
                && NPVARIANT_IS_STRING( npvPath ) 
                && NPVARIANT_IS_STRING( npvFormat ) && String::IsUtf8Ascii( formatString )
                && ( multipageIsNull || multipageIsBool )
                && formatIsString && String::IsUtf8Ascii( formatString ) )
            {
                wchar_t* wcPath = ScriptingUtils::npstringToWchar( &npvPath.value.stringValue );

                DocumentFormat format;
                int* indices = NULL;
                int indexCount = 0;

                // to array of string
                List<wchar_t*>* filenameArray = NULL;
                int filenameCount = 0;

                if ( !filenameIsNull && !filenameIsString && ScriptingUtils::npoToArray( m_npp, filenameObject, ArrayType::StringUtf16, ( void** ) &filenameArray ) /*&& ( filenameCount == indexCount )*/ )
                {
                    // filename array validated
                    filenameCount = filenameArray->count();
                }
                else
                {
                    /*filenameArray = new char*;
                    filenameArray[ 0 ] = NULL;
                    if ( filenameIsString )
                    {
                    char* filenameStringCopy = NULL;
                    copyString( &filenameStringCopy, filenameString );
                    filenameArray[ 0 ] = filenameStringCopy;
                    filenameCount = 1;
                    }*/
                }

                bool multipage = multipageIsNull ? false : multipageBool;
                bool pagesValid = false;
                if ( pagesIsInt )
                {
                    indices = new int;
                    *indices = pagesInt;
                    indexCount = 1;
                    pagesValid = true;
                }
                else if ( pagesIsString )
                {
                    pagesValid = pModel->parseIndices( pagesString, indices, indexCount );
                }

                if ( pModel->stringToDocumentFormat( formatString, format ) )
                {
                    //pModel->saveToFile( wcPath, format, indices, indexCount );

                    started = pModel->beginSave( format, multipage, wcPath, filenameArray, indices, indexCount, saveCallback, 0 );
                }
            }
            //NPN_ReleaseVariantValue( &npvPath );
            //NPN_ReleaseVariantValue( &npvFormat );
        }
        BOOLEAN_TO_NPVARIANT( started, *result );
        return true;
    }
    else if NS( saveToArray )
    {
        //
    }
    //else if NS( streamCreate ) int streamRead( int bytes ); streamClose();
    else if NS( zoom )
    {
    }
    else if NS( zoomIn )
    {
    }
    else if NS( zoomOut )
    {
    }
    else if NS( zoomReset )
    {
    }
    else if NS( panX )
    {
        if ( argCount == 1 )
        {
            NPVariant& arg0 = *( ( NPVariant* ) args );
            if ( IS_NUMERIC( arg0 ) )
            {
                pModel->panX( AS_INT( arg0 ) );
            }
            return true;
        }
    }
    else if NS( panY )
    {
        if ( argCount == 1 )
        {
            NPVariant& arg0 = *( ( NPVariant* ) args );
            if ( IS_NUMERIC( arg0 ) )
            {
                pModel->panY( AS_INT( arg0 ) );
            }
            return true;
        }
    }
    else if NS( pan )
    {
        if ( argCount == 2 )
        {
            NPVariant& arg0 = *( ( NPVariant* ) args );
            NPVariant& arg1 = *( ( NPVariant* ) ( args + 1 ) );

            if ( IS_NUMERIC( arg0 ) && IS_NUMERIC( arg1 ) )
            {
                pModel->pan( AS_INT( arg0 ), AS_INT( arg1 ) );

                return true;
            }
        }
    }
    else if NS( rotate )
    {
        if ( argCount == 1 )
        {
            NPVariant& npvArg0 = *( ( NPVariant* ) args );
            if ( IS_NUMERIC( npvArg0 ) )
            {
                int arg0 = AS_INT( npvArg0 );

                pModel->rotate( arg0 );

                return true;
            }
        }
    }
    else if NS( crop )
    {
        if ( !argCount )
        {
            pModel->crop();
        }
        return true;
    }
    else if NS( selectImage )
    {
        if ( argCount == 1 )
        {
            NPVariant& npvArg0 = *( ( NPVariant* ) args );
            if ( IS_NUMERIC( npvArg0 ) )
            {
                int arg0 = AS_INT( npvArg0 );
                pModel->selectImage( arg0 );

                pPlugin->onSelectedImageChanged( pModel->hasImage() ? pModel->getSelectedImageIndex() : -1 );

                return true;
            }
        }
    }
    else if NS( selectNextImage )
    {
        pModel->selectNextImage();

        pPlugin->onSelectedImageChanged( pModel->hasImage() ? pModel->getSelectedImageIndex() : -1 );

        return true;
    }
    else if NS( selectPreviousImage )
    {
        pModel->selectPreviousImage();

        m_pPlugin->onSelectedImageChanged( pModel->hasImage() ? pModel->getSelectedImageIndex() : -1 );

        return true;
    }
    else if NS( getCap )
    {
        NPVariant& resultValue = *result;
        VOID_TO_NPVARIANT( resultValue );

        if ( argCount == 1 )
        {
            NPVariant& npvArg0 = *( ( NPVariant* ) args );

            if ( IS_NUMERIC( npvArg0 ) )
            {
                int arg0 = AS_INT( npvArg0 );

                BaseCap* baseCap = pModel->getIaModel()->getCap( arg0 );

                NPObject* npobj = NULL;

                if ( npobj = ::NPN_CreateObject( m_npp, GET_NPOBJECT_CLASS( ScriptableDeviceCapability ) ) )
                {
                    ScriptableDeviceCapability* npoCap = ( ScriptableDeviceCapability* ) npobj;

                    //npoCap->setPlugin( this->m_pPlugin );
                    npoCap->setReference( baseCap );

                    //NPN_RetainObject( npobj );

                    OBJECT_TO_NPVARIANT( npobj, resultValue );

                    return true;
                }
            }
        }
    }
    else if NS( connect )
    {
        bool connected = m_pPlugin->m_model->connect();

        BOOLEAN_TO_NPVARIANT( connected, resultValue );

        return true;
    }
    else if NS( disconnect )
    {
        m_pPlugin->m_model->disconnect();

        return true;
    }
    else if NS( clear )
    {
        pModel->clearImages();

        return true;
    }
    else if NS( getFilter ) //parameters: format, pixelType
    {
        if( argCount == 2 )
        {
            NPVariant npvFormat = *args, npvPixelType = *( args + 1 );
            if ( NPVARIANT_IS_STRING( npvFormat ) && NPVARIANT_IS_INT32( npvPixelType ) )
            {
                wchar_t* wcFormat = ScriptingUtils::npstringToWchar( &npvFormat.value.stringValue );
                int pixelType = NPVARIANT_TO_INT32( npvPixelType );

                DocumentFormat format;
                if ( pModel->stringToDocumentFormat( wcFormat, format ) && pixelType >= ( int ) PixelType::Bw && pixelType <= ( int ) PixelType::Rgb )
                {
                    ImageFilter filter = pModel->getFilter( format, ( PixelType ) pixelType );
                    INT32_TO_NPVARIANT( ( int ) filter, resultValue );
                }
                delete wcFormat;

                return true;
            }
        }
    }
    else if NS( setFilter ) //parameters: format, pixelType, filter
    {
        if( argCount == 3 )
        {
            const NPVariant& npvFormat = *args;
            const NPVariant& npvPixelType = *( args + 1 );
            const NPVariant& npvFilter = *( args + 2 );

            if ( NPVARIANT_IS_STRING( npvFormat ) && NPVARIANT_IS_INT32( npvPixelType ) && NPVARIANT_IS_INT32( npvFilter ) )
            {
                wchar_t* wcFormat = ScriptingUtils::npstringToWchar( &npvFormat.value.stringValue );
                int pixelType = NPVARIANT_TO_INT32( npvPixelType );
                int filter = NPVARIANT_TO_INT32( npvFilter );

                DocumentFormat format;
                if ( pModel->stringToDocumentFormat( wcFormat, format ) 
                    && pixelType >= ( int ) PixelType::Bw && pixelType <= ( int ) PixelType::Rgb 
                    && filter >= ( int ) ImageFilter::Flate && filter <= ( int ) ImageFilter::DCT )
                {
                    pModel->setFilter( format, ( PixelType ) pixelType, ( ImageFilter ) filter );
                }
                delete wcFormat;

                return true;
            }
        }
    }
    else if NS( resume )
    {
        if ( argCount == 1 )
        {
            const NPVariant& npvProceed = *args;

            if ( NPVARIANT_IS_BOOLEAN( npvProceed ) )
            {
                bool proceed = npvProceed.value.boolValue;

                pModel->resumeScan( proceed );

                return true;
            }
        }
    }
    else if NS( stop )
    {
        pModel->stopScan();

        return true;
    }
    else if NS( pause )
    {
        pModel->pauseScan();

        return true;
    }
    else if NS( testMethod )
    {
        //char* value = NULL;
        ////memset( value, 0, 1024 );
        //uint32_t length;

        //int i = NPN_GetValueForURL( m_npp, NPNURLVCookie, "http://localhost:2544/default.aspx", &value, &length ); //

        return true;
    }
    else if NS( initializeDevice )
    {
        pModel->initializeDevice();

        return true;
    }
#if defined(_DEBUG) || defined(_ADDON)
    else if NS( getFolder )
    {
        IFileDialog *pfd = NULL;
        if ( SUCCEEDED( ::CoCreateInstance( CLSID_FileOpenDialog, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS( &pfd ) ) ) )
        {
            DWORD dwOptions = 0;
            WCHAR *path = NULL;
            if ( SUCCEEDED( pfd->GetOptions( &dwOptions ) ) )
            {
                pfd->SetOptions( dwOptions | FOS_PICKFOLDERS | FOS_PATHMUSTEXIST | FOS_DONTADDTORECENT );
            }
            if ( SUCCEEDED( pfd->Show( NULL ) ) )
            {
                IShellItem *psi = NULL;
                if ( SUCCEEDED( pfd->GetResult( &psi ) ) )
                {
                    if ( SUCCEEDED( psi->GetDisplayName( SIGDN_DESKTOPABSOLUTEPARSING, &path ) ) )
                    {
                        STRINGZ_TO_NPVARIANT( ScriptingUtils::npstring( path ), resultValue );
                        //delete path;
                    }
                    psi->Release();
                }
            }
            pfd->Release();
            return true;
        }
    }
    else if NS( openFolder )
    {
        if( argCount == 1 )
        {
            NPVariant npvPath = *args;

            bool pathIsString = NPVARIANT_IS_STRING( npvPath );
            char* pathString = NULL;
            if ( pathIsString )
            {
                wchar_t* wcPath = ScriptingUtils::npstringToWchar( &npvPath.value.stringValue );
                size_t pathLen = String::GetLength( wcPath );
                wchar_t* wcParams = new wchar_t[ ( pathLen + 16 ) << 1 ];
                ::memset( wcParams, 0, ( pathLen + 16 ) << 1 );
                ::memcpy( wcParams, L"/select, \"", 20 );
                ::memcpy( wcParams + 10, wcPath, pathLen << 1 );
                ::memcpy( wcParams + 10 + pathLen, L"\"", 2 );

                ::ShellExecuteW( NULL, L"open", L"explorer.exe", wcParams, NULL, SW_NORMAL );
                delete wcPath;
                delete wcParams;

                return true;
            }
        }
    }
#endif
    else if NS( focus )
    {
        pPlugin->setFocus();
        return true;
    }
    else if NS( deleteImage )
    {
        if ( argCount <= 1 )
        {
            pModel->deleteImage( pModel->getSelectedImageIndex() );
            return true;
        }
    }
    else if NS( shiftImage )
    {
        if ( argCount == 1 )
        {
            const NPVariant& v = *args;
            if ( IS_NUMERIC( v ) )
            {
                int intValue = AS_INT( v );

                pModel->shiftImage( intValue );

                return true;
            }
        }
    }
    else if NS( moveImage )
    {
    }

    return false;
}

/*bool ScriptablePluginObject::invokeDefault( const NPVariant *args, uint32_t argCount, NPVariant *result )
{
// ScriptablePluginObject default method called
//STRINGZ_TO_NPVARIANT(_strdup("default method return val"), *result);

// default method not supported
//STRINGZ_TO_NPVARIANT(npstring("default method return val"), *result);

return true;
}*/
